/*
 * Configuration store for different profiles. Loads data from a configuration 
 * file and stores current values using the preferences-api. New configuration 
 * files can saved.
 */
package com.google.cse.emea.jct.utils;

import com.google.cse.emea.universaltransformer.TaskList;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Properties;
import java.util.Vector;

/**
 *
 * @author jtrapp@google.com
 */
public class Profile {

    private static HashMap<String, Profile> profiles = new HashMap<String, Profile>();

    private java.util.prefs.Preferences prefs = null;
    private java.util.Properties props = null; //external config file.
    public String profile = null;
    private static final String HISTORY = ".history";
    public static final String CLASSPATH_PROTOCOL = "cpath://";
    public static final String KEY_PROFILE_NAME = "name";
    public static final String KEY_PROFILE_VERSION = "version";
    public static final String KEY_CONFIGURATION_URL = "configurationURL";

    /** initialize property profile */
    /** initialize property profile */
    private Profile(Class clazz, String configurationURL) throws IOException {
        prefs = java.util.prefs.Preferences.userNodeForPackage(clazz);

        loadConfiguration(configurationURL);
    }

    /** initialize property profile */
    public Profile(Class clazz, File file) throws IOException {
        prefs = java.util.prefs.Preferences.userNodeForPackage(clazz);

        props.load(new FileInputStream(file));
    }

    public static String get(Class clazz, String profileURL, String key) throws IOException {
        Profile profile = new Profile(clazz, profileURL);
        String value = profile.props.getProperty(key);
        return value;
    }

    public String get(ProfileKeysInterface key, int i, String label) {
        return get(key.getName()+i+label);
    }

    public String[] getArray(ProfileKeysInterface key, String splitRegex) {
        String value = get(key);
        if (value!=null){
            return value.split(splitRegex);
        }
        return null;
    }

    public boolean getBoolean(TaskList key, boolean b) {
        return getBoolean(key.getName(), b);
    }


    public HashMap<String, String> loadTable(ProfileKeysInterface key) {
        return loadTable(key.getName());
    }
        
    /** Load a list of values.*/
    public HashMap<String, String> loadTable(final String key) {
        HashMap<String, String> result = new HashMap<String, String>();
        String prefix = key + ".";
        int startIndex = prefix.length();
        for (String property : props.stringPropertyNames()) {
            if (property.startsWith(prefix)){
                String value = (String) props.get(key);
                result.put(property.substring(startIndex), value);
            }
        }
        return result;
    }

    public void setConfiguration(String configurationURL) throws IOException {
        loadConfiguration(configurationURL);

        prefs.put(KEY_CONFIGURATION_URL, configurationURL);
        String version = props.getProperty(KEY_PROFILE_VERSION);
        if (version != null) {
            prefs.put(KEY_PROFILE_VERSION, version);
        }
    }

    public boolean getBoolean(String name) {
        return getBoolean(name, false);
    }


    /** Load a configuration file.
     * @param configurationURL either a URL a file, or a classpath resource. 
     * If a classpath-resource is used, Append "cpath:///" as a protocoll
     */
    private void loadConfiguration(String configurationURL) throws IOException {
        props = new Properties();
        if ((configurationURL == null) || (configurationURL.length() == 0)) {
            throw new IOException("Missing Configuration file");
        }

        if (configurationURL.startsWith(CLASSPATH_PROTOCOL)) {
            InputStream stream = this.getClass().getResourceAsStream(
                    configurationURL.substring(CLASSPATH_PROTOCOL.length()));
            if (stream == null) {
                throw new IOException("Can't open connection to " + configurationURL);
            }
            props.load(stream);
        } else {
            try {
                URL url = new URL(configurationURL);
                props.load(url.openStream());
            } catch (MalformedURLException murle) {
                props.load(new FileInputStream(configurationURL));
            }
        }
        this.profile = props.getProperty(KEY_PROFILE_NAME);

    }

    /** 
     * Store the current configuration under a new name.
     */
    public void storeConfiguration(String newName, String filename) throws FileNotFoundException, IOException {
        props.put(KEY_PROFILE_NAME, newName);
        props.list(new PrintStream(new FileOutputStream(filename)));
        prefs.put(KEY_PROFILE_NAME, newName);
    }

    /** 
     * Load setting or Configuration 
     */
    public String get(ProfileKeysInterface key) {
        return get(key.getName());
    }

    /** 
     * Load setting or Configuration 
     */
    public String get(String key) {
        return prefs.get(build(key), props.getProperty(key));
    }

    /** 
     * Load setting or Configuration 
     */
    public String get(ProfileKeysInterface key, String defaultValue) {
        return get(key.getName(), defaultValue);
    }

    /** 
     * Load setting or Configuration 
     */
    public String get(String key, String defaultValue) {
        String value = get(key);
        if (value == null || value.trim().length() == 0) {
            return defaultValue;
        } else {
            return value.trim();
        }

    }

    /**
     * 
     * @param key
     * @return
     */
    public int getInteger(ProfileKeysInterface key) {
        return getInteger(key.getName());
    }

    /**
     * 
     * @param key
     * @return
     */
    public int getInteger(String key) {
        String value = get(key);
        return Integer.parseInt(value);
    }

    public boolean getBoolean(ProfileKeysInterface key) {
        return getBoolean(key.getName());
    }

    public boolean getBoolean(String key, boolean defaultValue) {
        String value = get(key);
        if (value != null) {
            if (value.length() == 0) {
                return true;
            } else {
                char c = value.charAt(0);
                if ("1tTyYjJ".indexOf(c) > 0) {
                    return true;
                }
            }
        }
        return defaultValue;
    }

    public void put(ProfileKeysInterface key, boolean value) {
        put(key.getName(), value);
    }

    public void put(ProfileKeysInterface key, String value) {
        put(key.getName(), value);
    }

    public Vector<String> loadHistory(final ProfileKeysInterface prefix, final int maxentries) {
        return loadHistory(prefix.getName(), maxentries);
    }

    public void updateHistory(final ProfileKeysInterface prefix, Vector<String> history, final String lastValue) {
        updateHistory(prefix.getName(), history, lastValue);
    }

    /** Store values in the preferencs and in the configuration. Preferences will
    automatically retrieved, wen reloaded. The configuration can be stored, 
    if you want to exchange the configuration with somebody else.*/
    public void put(String key, boolean value) {
        prefs.put(build(key), value + "");
        props.put(key, value + "");
    }

    /** Store values in the preferencs and in the configuration. Preferences will
    automatically retrieved, wen reloaded. The configuration can be stored, 
    if you want to exchange the configuration with somebody else.*/
    public void put(String key, String value) {
        prefs.put(build(key), value);
        props.put(key, value);
    }

    public Vector<String> loadHistory(final String prefix, final int maxentries) {
        Vector<String> history = new Vector<String>();
        String prefix_history = prefix + HISTORY + ".";
        for (int i = 0; i < maxentries; i++) {
            String entry = get(prefix_history + i);
            if (entry == null || entry.length() == 0) {
                return history;
            }
            history.add(entry);
        }
        return history;
    }

    /** 
    Add a new value and store it in the history
     */
    public void updateHistory(final String prefix, Vector<String> history, final String lastValue) {
        String prefix_history = prefix + HISTORY + ".";
        if (history.contains(lastValue)) {
            history.remove(lastValue);
        }
        while (history.size() >= 10) {
            history.remove(history.size() - 1);
        }
        history.add(0, lastValue);

        for (int i = 0; i < history.size(); i++) {
            put(prefix_history + i, history.get(i));
        }
    }

    /** Get Element for specified index.*/
    public String getElementAt(final String key, int index) {
        String fullKey = key + "." + index;

        String entry = get(fullKey);
        if ((entry == null) || (entry.length() == 0)) {
            return entry;
        }
        return null;
    }

    /** Get the first element.*/
    public String getFirst(final String key) {
        return getElementAt(key, 0);
    }

        /** Load a list of values.*/
    public Vector<String> loadList(ProfileKeysInterface key) {
        return loadList(key.getName());
    }
    
    /** Load a list of values.*/
    public Vector<String> loadList(final String key) {
        Vector<String> set = new Vector<String>();
        String prefix = key + ".";
        for (int i = 0; i < 99999; i++) {
            String entry = get(prefix + i);
            if ((entry == null) || (entry.length() == 0)) {
                return set;
            }
            set.add(entry);
        }
        return set;
    }

    /** 
    Store the list.
     */
    public void updatelist(Vector<String> set, final String key) {
        String prefix = key + ".";

        for (int i = 0; i < set.size(); i++) {
            put(prefix + i, set.get(i));
        }
    }

    private String build(String key) {
        return profile + "." + key;
    }

    /** Profile name*/
    public String getName() {
        return profile;
    }

    /** Profile Version*/
    public String getVersion() {
        return prefs.get(KEY_PROFILE_VERSION, "0");
    }

    /** Save the curent profile.*/
    public void save(File file) throws IOException {
        FileOutputStream out = new FileOutputStream(file);
        PrintStream pout = new PrintStream(out);
        props.list(pout);
        pout.close();
        out.close();
    }

    @Override
    public String toString() {
        return "Profile: " + getName();
    }
    
    
    public static Profile get(Class scope, String defaultTaskURL) throws IOException {
        Profile profile = profiles.get(defaultTaskURL);
        if (profile == null) {
            profile = new Profile(scope, defaultTaskURL);
            profiles.put(defaultTaskURL, profile);
        }
        return profile;
    }
}
